package com.diaohw.platform.framework.web.advice;

import cn.hutool.core.convert.Convert;
import com.diaohw.platform.common.enums.CodeMsgEnum;
import com.diaohw.platform.common.exception.GlobalException;
import com.diaohw.platform.common.obj.Result;
import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@Slf4j
@Configuration
@RestControllerAdvice("com.diaohw")
public class GlobalAdvice {

    /**
     * date localTime localDate localDateTime 处理
     */
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                LocalDateTime localDateTime = Convert.toLocalDateTime(text);
                if (localDateTime != null) {
                    setValue(localDateTime.toLocalDate());
                } else {
                    setValue(null);
                }
            }
        });
        binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(Convert.toLocalDateTime(text));
            }
        });
        binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                LocalDateTime localDateTime = Convert.toLocalDateTime(text);
                if (localDateTime != null) {
                    setValue(localDateTime.toLocalTime());
                } else {
                    setValue(null);
                }
            }
        });
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(Convert.toDate(text));
            }
        });
    }

    // 自定义异常
    @ExceptionHandler(GlobalException.class)
    public Result<?> global(GlobalException e) {
        return e.getResult();
    }

    // 自定义异常
    @ExceptionHandler(JsonProcessingException.class)
    public Result<?> jsonProcess(JsonProcessingException e) {
        return unifiedProcessing(e, CodeMsgEnum.JSON.status);
    }

    /**
     * handleHttpMessageNotReadableException
     * 参数序列化失败异常劫持
     *
     * @param e       HttpMessageNotReadableException
     * @param request {@link HttpServletRequest}
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
        String requestUri = request.getRequestURI();
        log.warn("请求地址‘{}’,参数序列化失败，‘{}’", requestUri, e.getMessage());
        return unifiedProcessing(e, CodeMsgEnum.PARAMS.status);
    }

    /**
     * handleHttpRequestMethodNotSupportedException
     * 请求方式不支持异常劫持
     *
     * @param e       HttpRequestMethodNotSupportedException
     * @param request {@link HttpServletRequest}
     * @author chengtt
     * @date 2023/8/29 13:12
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String requestUri = request.getRequestURI();
        log.warn("请求地址'{}',不支持'{}'请求", requestUri, e.getMethod());
        return Result.fail(CodeMsgEnum.URL.status, e.getMessage());
    }

    /**
     * handleConstraintViolationException
     * 单个参数异常处理
     *
     * @param e {@link ConstraintViolationException
     * @author chengtt
     * @date 2023/8/29 13:14
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<?> handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder errorMessage = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> item : violations) {
            errorMessage.append(item.getMessage()).append("/");
        }

        return Result.fail(CodeMsgEnum.DATA.status, errorMessage.toString());
    }

    /**
     * handleBindException
     * form表单方式提交参数自定义异常
     *
     * @param ex {@link BindException}
     * @author chengtt
     * @date 2023/8/29 13:14
     */
    @ExceptionHandler(BindException.class)
    public Result<?> handleBindException(BindException ex) {
        StringBuilder errorMessage = new StringBuilder();

        BindingResult bindingResult = ex.getBindingResult();
        // 获取所有的错误信息
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        // 拼接错误信息
        allErrors.forEach(e -> {
            if (e instanceof FieldError) {
                errorMessage.append(((FieldError) e).getField()).append(e.getDefaultMessage());
            } else {
                errorMessage.append(e.getDefaultMessage());
            }
        });
        log.warn("参数格式异常: {}", errorMessage);
        return Result.fail(CodeMsgEnum.DATA.status, errorMessage.toString());
    }

    /**
     * handleMethodArgumentNotValidException
     * json方式提交参数自定义异常
     *
     * @param e {@link MethodArgumentNotValidException}
     * @author chengtt
     * @date 2023/8/29 13:15
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder errorMessage = new StringBuilder();
        for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
            errorMessage.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append("/");
        }
        //条件自定义参数校验
        for (ObjectError objectError : e.getBindingResult().getAllErrors()) {
            String[] codes = objectError.getCodes();
            if (codes != null && Objects.equals("ScriptAssert", codes[codes.length - 1])) {
                errorMessage.append(objectError.getDefaultMessage()).append("/");
            }
        }
        log.warn("参数异常: method: {}, result: {}", e.getBindingResult().getObjectName(), errorMessage);
        return Result.fail(CodeMsgEnum.DATA.status, errorMessage.toString());
    }

    /**
     * handleIllegalArgumentException
     * 不合法参数
     *
     * @param e {@link IllegalArgumentException}
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<?> handleIllegalArgumentException(IllegalArgumentException e) {
        log.warn("参数非法:", e);
        return Result.fail(CodeMsgEnum.DATA.status, e.getMessage());
    }

    /**
     * handleNoHandlerFoundException
     * 方法访问地址错误异常
     *
     * @param e {@link NoHandlerFoundException}
     * @author chengtt
     * @date 2023/8/29 13:16
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public Result<?> handleNoHandlerFoundException(NoHandlerFoundException e) {
        log.warn("方法路径异常:", e);
        return Result.fail(CodeMsgEnum.URL.status, e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<?> IO(Exception e) {
        return unifiedProcessing(e, CodeMsgEnum.EXCEPTION.status);
    }


    private Result<?> unifiedProcessing(Exception e, String status) {
        e.printStackTrace();
        return Result.fail(status, e.getMessage());
    }

}
